iT邦幫忙

1

Day 11 Alert Deduplication:偵測到了,不代表每次都要發警報

  • 分享至 

  • xImage
  •  

昨天 Day 10,我把 File Monitor 正式接上 Detection Engine,讓 BehaviorGuard 可以即時監控檔案異動,並產生:

  • BG-FILE-001:Sensitive File Modification
  • BG-FILE-002:Sensitive File Deletion
  • BG-FILE-003:Suspicious Script Creation
  • BG-FILE-004:Hidden File Creation

不過實際測試之後,我發現了一個新的問題。

同一個檔案明明只做了一次操作,File Monitor 有時卻會收到不只一個 MODIFIED Event。

例如:

MODIFIED
→ BG-FILE-001 HIGH

MODIFIED
→ BG-FILE-001 HIGH

如果未來 BehaviorGuard 監控的事件越來越多,這種情況可能會產生大量重複 Alert。

所以今天的目標不是繼續增加 Detection Rule,而是開始處理:

Alert Deduplication / Alert Suppression

也就是:

事件還是要持續監控、Detection Rule 還是要持續判斷,但是短時間內完全相同的 Alert,不需要一直通知。


一、Telemetry 跟 Alert 是不一樣的

今天我先理解了一個很重要的觀念:

Telemetry 數量不等於 Alert 數量。

例如系統可能收到:

MODIFIED
MODIFIED
MODIFIED
MODIFIED
MODIFIED

這五筆都是系統實際觀察到的 Telemetry,所以不能因為怕 Alert 太多,就乾脆停止監控。

比較合理的流程應該是:

File System
    ↓
watchdog
    ↓
File Telemetry
    ↓
Detection Engine
    ↓
Rule Match
    ↓
Alert Manager
    ↓
Deduplication / Suppression
    ↓
真正需要輸出的 Alert

也就是 Detection Engine 負責:

這個行為有沒有符合偵測規則?

Alert Manager 則負責:

雖然規則命中了,但是這個 Alert 有沒有必要再次通知?

這兩件事情其實是不同的。


二、建立 Alert Manager

今天新增:

detection/alert_manager.py

目前我設定:

SUPPRESSION_WINDOW = 5

也就是相同 Alert 在 5 秒內再次出現,就先 Suppress。

完整程式:

import time


# Store information about previously emitted alerts.
alert_history = {}


# Suppress identical alerts within this window.
SUPPRESSION_WINDOW = 5


def process_alert(alert, event):
    """
    Process an alert and decide whether it should be emitted.

    Alerts with the same:
        rule_id + action + path

    are treated as duplicates within the suppression window.

    Returns:
        {
            "emit": True/False,
            "suppressed_count": int
        }
    """

    rule_id = alert.get("rule_id", "")
    action = event.get("action", "")
    path = event.get("path", "")

    alert_key = (
        rule_id,
        action,
        path
    )

    now = time.time()

    previous = alert_history.get(alert_key)

    # First occurrence
    if previous is None:

        alert_history[alert_key] = {
            "last_emit_time": now,
            "suppressed_count": 0
        }

        return {
            "emit": True,
            "suppressed_count": 0
        }

    elapsed = now - previous["last_emit_time"]

    # Duplicate inside suppression window
    if elapsed < SUPPRESSION_WINDOW:

        previous["suppressed_count"] += 1

        return {
            "emit": False,
            "suppressed_count": previous["suppressed_count"]
        }

    # Suppression window expired
    old_suppressed_count = previous["suppressed_count"]

    alert_history[alert_key] = {
        "last_emit_time": now,
        "suppressed_count": 0
    }

    return {
        "emit": True,
        "suppressed_count": old_suppressed_count
    }

三、什麼叫做「相同 Alert」?

目前 BehaviorGuard 使用三個資訊建立 Alert Key:

Rule ID
+
Action
+
Path

例如:

BG-FILE-001
MODIFIED
/tmp/behaviorguard_test/etc/passwd

會組成一個識別 Key。

如果 5 秒內再次出現完全相同的組合:

BG-FILE-001
MODIFIED
/tmp/behaviorguard_test/etc/passwd

BehaviorGuard 就知道:

這很可能是剛才那個事件造成的重複 Alert。

因此進行 Suppression。

但是如果出現:

BG-FILE-002
DELETED
/tmp/behaviorguard_test/etc/passwd

就不能 Suppress。

因為「修改 passwd」跟「刪除 passwd」明顯不是同一件事情。


四、把 Alert Manager 接到 File Monitor

接著修改:

agent/file_monitor.py

完整版本如下:

import time

from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler

from detection.engine import analyze_file_event
from detection.alert_manager import process_alert


WATCH_PATH = "/tmp/behaviorguard_test"


class BehaviorGuardFileHandler(FileSystemEventHandler):

    def print_event(self, action, path):
        print("\n========== BehaviorGuard File Event ==========")
        print(f"Action : {action}")
        print(f"Path   : {path}")
        print("==============================================")

    def print_alert(self, alert, event):
        print("\n========== BehaviorGuard File Alert ==========")
        print(f"Rule        : {alert['rule_id']}")
        print(f"Name        : {alert['rule_name']}")
        print(f"Severity    : {alert['severity']}")
        print(f"Action      : {event['action']}")
        print(f"Path        : {event['path']}")
        print(f"Description : {alert['description']}")
        print("==============================================")

    def process_event(self, action, path):

        event = {
            "action": action,
            "path": path
        }

        # Raw telemetry
        self.print_event(action, path)

        # Detection Engine
        alerts = analyze_file_event(event)

        # Alert Manager
        for alert in alerts:

            result = process_alert(alert, event)

            if result["emit"]:

                self.print_alert(alert, event)

                if result["suppressed_count"] > 0:
                    print(
                        f"[BehaviorGuard] "
                        f"{result['suppressed_count']} duplicate "
                        f"alert(s) were suppressed during "
                        f"the previous window."
                    )

            else:

                print(
                    f"[SUPPRESSED] "
                    f"{alert['rule_id']} duplicate alert "
                    f"for {path} "
                    f"(count: {result['suppressed_count']})"
                )

    def on_created(self, event):
        if event.is_directory:
            return

        self.process_event(
            "CREATED",
            event.src_path
        )

    def on_modified(self, event):
        if event.is_directory:
            return

        self.process_event(
            "MODIFIED",
            event.src_path
        )

    def on_deleted(self, event):
        if event.is_directory:
            return

        self.process_event(
            "DELETED",
            event.src_path
        )

    def on_moved(self, event):
        if event.is_directory:
            return

        self.process_event(
            "MOVED",
            event.dest_path
        )


def monitor_files():

    print("[BehaviorGuard] File Monitor started...")
    print(f"[BehaviorGuard] Watching: {WATCH_PATH}")

    event_handler = BehaviorGuardFileHandler()

    observer = Observer()

    observer.schedule(
        event_handler,
        WATCH_PATH,
        recursive=True
    )

    observer.start()

    try:
        while True:
            time.sleep(1)

    except KeyboardInterrupt:
        print("\n[BehaviorGuard] File Monitor stopped.")
        observer.stop()

    observer.join()


if __name__ == "__main__":
    monitor_files()

五、實際測試

啟動:

PYTHONPATH=. python agent/file_monitor.py

接著建立測試用的 passwd:

mkdir -p /tmp/behaviorguard_test/etc
touch /tmp/behaviorguard_test/etc/passwd

然後短時間內連續修改:

echo "1" >> /tmp/behaviorguard_test/etc/passwd
echo "2" >> /tmp/behaviorguard_test/etc/passwd
echo "3" >> /tmp/behaviorguard_test/etc/passwd
echo "4" >> /tmp/behaviorguard_test/etc/passwd

這裡修改的是:

/tmp/behaviorguard_test/etc/passwd

並不是真正系統的:

/etc/passwd

所以可以安全地在 Lab 裡進行測試。


六、測試結果

第一次修改時:

========== BehaviorGuard File Alert ==========
Rule        : BG-FILE-001
Name        : Sensitive File Modification
Severity    : HIGH
Action      : MODIFIED
Path        : /tmp/behaviorguard_test/etc/passwd
Description : Sensitive system file /etc/passwd was modified.
==============================================

但是接下來短時間內又收到相同事件時:

[SUPPRESSED] BG-FILE-001 duplicate alert for /tmp/behaviorguard_test/etc/passwd (count: 1)

[SUPPRESSED] BG-FILE-001 duplicate alert for /tmp/behaviorguard_test/etc/passwd (count: 2)

[SUPPRESSED] BG-FILE-001 duplicate alert for /tmp/behaviorguard_test/etc/passwd (count: 3)

[SUPPRESSED] BG-FILE-001 duplicate alert for /tmp/behaviorguard_test/etc/passwd (count: 4)

這代表 Alert Manager 成功運作。

整個過程變成:

第一次 BG-FILE-001
        ↓
      ALERT


第二次 BG-FILE-001
        ↓
    SUPPRESSED
     count = 1


第三次 BG-FILE-001
        ↓
    SUPPRESSED
     count = 2


第四次 BG-FILE-001
        ↓
    SUPPRESSED
     count = 3

而且 BehaviorGuard 並沒有停止監控。

File Event 還是持續被收集,只是重複的 Alert 被抑制。


七、為什麼不能直接 sleep(5)?

一開始可能會想到:

time.sleep(5)

發完 Alert 之後休息五秒,不就不會一直 Alert 了?

但這其實有問題。

因為如果整個 Monitor 停止五秒:

發現 BG-FILE-001
        ↓
sleep 5 秒
        ↓
其他事件?

這段時間可能還有其他重要事件需要處理。

例如:

/etc/passwd MODIFIED
        ↓
BG-FILE-001 HIGH

1 秒後

/etc/passwd DELETED
        ↓
BG-FILE-002 CRITICAL

如果因為第一個 Alert 就讓整個 Monitor 暫停,反而可能影響後續事件處理。

所以今天採用的方式是:

Monitor 持續運作
        ↓
Telemetry 持續收集
        ↓
Detection Rule 持續分析
        ↓
Alert Manager 判斷
        ↓
只 Suppress 重複 Alert

也就是:

Suppress Alert,而不是停止 Detection。


八、今天學到的資安觀念

今天最大的收穫是,我開始發現 Detection 不只是「寫規則」。

前幾天我的想法比較像:

發現可疑行為
↓
Rule Match
↓
Alert

但是當監控的 Process、Network、File Event 越來越多之後,就會開始遇到另一個問題:

Alert Noise。

如果一個行為產生十幾個重複 Alert,分析人員看到的可能不是更多資訊,而只是更多雜訊。

因此比較完整的流程應該是:

Telemetry
    ↓
Detection Engine
    ↓
Rule Match
    ↓
Alert Manager
    ↓
Deduplication
    ↓
Suppression
    ↓
Alert

而且 Suppression 也不能把資訊完全丟掉。

所以今天又加入:

suppressed_count

即使 5 秒內壓掉四個重複 Alert,我們還是知道:

Suppressed Count = 4

這樣才能同時做到:

降低 Alert Noise + 保留事件發生頻率。


Day 11 完成

今天沒有新增更多 Detection Rule,而是開始改善 BehaviorGuard 的 Alert Pipeline。

目前架構:

Process Monitor ────┐
Command Monitor ────┤
Network Monitor ────┼──→ Detection Engine
File Monitor ───────┘
                           ↓
                       Rule Match
                           ↓
                     Alert Manager
                           ↓
                 Deduplication / Suppression
                           ↓
                         Alert

目前 Alert Manager 先接在 File Monitor 上進行驗證,後續還可以再逐步讓其他 Telemetry 共用這套機制。

今天完成:

  • Alert Manager
  • Alert Deduplication
  • Suppression Window
  • Alert Key
  • suppressed_count
  • File Monitor 整合
  • 重複 Alert 測試

做到這裡,我也開始理解一件事情:

好的偵測系統不只是「能不能抓到」,還要考慮「抓到之後產生的資訊有沒有辦法被使用」。

如果每一個底層事件都變成一個 Alert,最後反而可能讓真正重要的警報被淹沒。

下一步再繼續擴充 BehaviorGuard 的 Detection 能力。


圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言